package com.flex.reflection;

import com.flex.reflection.handler.Handler;
import com.flex.reflection.resolver.ParamterResolveType;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 方法元数据
 * @author ganlt
 * @since 2019/09/20 13:04
 */
public class MetaMethod implements MetaObj,MethodInvoke{
    /**
     * 参数
     */
    private Map<String, Type> paramMap = new HashMap<>();

    private List<Handler> handlers = new ArrayList<>();
    /**
     * 定义注解
     */
    private List<Annotation> annotations;
    /**
     * 方法
     */
    private Method method;
    /**
     * 所属类实例
     */
    private Object instance;
    /**
     * 返回类型
     */
    private Type returnType;

    private MetaClass.Config config;

    public MetaMethod(Method method, Object instance) {
        this.method = method;
        this.instance = instance;
    }

    public Method getMethod() {
        return method;
    }

    @Override
    public AnnotationEntity getAnnotationEntity(Class<? extends Annotation> annotation) {
        Annotation declaredAnnotation = this.method.getDeclaredAnnotation(annotation);
        AnnotationEntity annoEntityObj = new AnnotationEntity(this.config, declaredAnnotation);
        return annoEntityObj;
    }

    @Override
    public boolean hasAnnotation(Class<? extends Annotation> annotation) {
        return this.method.isAnnotationPresent(annotation);
    }

    @Override
    public List<Type> paramsType() {
        ParamterResolveType paramterResolveType = new ParamterResolveType();
        Type[] types = paramterResolveType.resolveParamTypes(this.method, this.instance.getClass());
        return new ArrayList<>(Arrays.asList(types));
    }

    @Override
    public Type returnType() {
        ParamterResolveType paramterResolveType = new ParamterResolveType();
        return paramterResolveType.resolveReturnType(this.method, this.instance.getClass());
    }

    @Override
    public Object invoke(Object... args) {
        try {
            return this.method.invoke(this.instance, args);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            if (MetaClass.canControlMemberAccessible()) {
                this.method.setAccessible(true);
                try {
                    return this.method.invoke(this.instance, args);
                } catch (IllegalAccessException | InvocationTargetException ex) {
                    throw new RuntimeException("调用方法失败");
                }
            }
            e.printStackTrace();
        }
        return null;
    }


    public MetaClass.Config getConfig() {
        return config;
    }

    public void setConfig(MetaClass.Config config) {
        this.config = config;
    }
}
